package com.atguigu.gmall.product.service.impl;

import com.atguigu.gmall.common.cache.GmallCache;
import com.atguigu.gmall.common.constant.RedisConst;
import com.atguigu.gmall.model.product.*;
import com.atguigu.gmall.product.mapper.*;
import com.atguigu.gmall.product.service.ProductService;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * @Author: lx
 * @Time: 15:31
 * @Description:
 */
@Service
public class ProductServiceImpl implements ProductService {


    @Autowired
    private BaseCategoryViewMapper baseCategoryViewMapper;
    @Autowired
    private SkuInfoMapper skuInfoMapper;
    @Autowired
    private SkuImageMapper skuImageMapper;
    @Autowired
    private RedisTemplate redisTemplate;

    //查询一二三级分类
    @Override
    public BaseCategoryView getBaseCategoryView(Long category3Id) {
        return baseCategoryViewMapper.selectById(category3Id);
    }

    //查询SkuInfo对象
    @Override
    @GmallCache(prefix = "getSkuInfo")
    public SkuInfo getSkuInfo(Long skuId) {
        SkuInfo skuInfo = skuInfoMapper.selectById(skuId);
        //判断是否NULL
        if (null != skuInfo) {
            List<SkuImage> skuImageList = skuImageMapper.
                    selectList(new QueryWrapper<SkuImage>().eq("sku_id", skuId));
            skuInfo.setSkuImageList(skuImageList);
        }
        return skuInfo;
    }


    @Autowired
    private RedissonClient redissonClient;

    //查询SkuInfo对象
    public SkuInfo getSkuInfo111(Long skuId) {
        //Redisson来解决分布式锁
        //1:查询Redis  K: sku:skuId:info
        String cacheKey = RedisConst.SKUKEY_PREFIX + skuId + RedisConst.SKUKEY_SUFFIX;
        //锁的Key
        String lockKey = RedisConst.SKUKEY_PREFIX + skuId + RedisConst.SKULOCK_SUFFIX;
        SkuInfo skuInfo = (SkuInfo) redisTemplate.opsForValue().get(cacheKey);
        if (null == skuInfo) {
            //参数1： 过期不候  1S
            //参数2： 获取到了锁  让锁什么时候过期
            RLock rLock = redissonClient.getLock(lockKey);
            try {
                boolean lock = rLock.
                        tryLock(1, 1, TimeUnit.SECONDS);
                if (lock) {
                    try {
                        //获取到锁的
                        //3:没有 直接查询DB
                        skuInfo = skuInfoMapper.selectById(skuId);
                        //判断是否NULL
                        if (null != skuInfo) {
                            List<SkuImage> skuImageList = skuImageMapper.
                                    selectList(new QueryWrapper<SkuImage>().eq("sku_id", skuId));
                            skuInfo.setSkuImageList(skuImageList);
                            //4:保存缓存中一份 100万次-1
                            redisTemplate.opsForValue().set(cacheKey, skuInfo,
                                    RedisConst.SKUKEY_TIMEOUT + new Random().nextInt(300)
                                    , TimeUnit.SECONDS);
                        } else {
                            //4:保存缓存中一份  空结果也进行缓存，但它的过期时间会很短，最长不超过五分钟。
                            skuInfo = new SkuInfo();
                            redisTemplate.opsForValue().set(cacheKey, skuInfo, 5
                                    , TimeUnit.MINUTES);
                        }
                    } finally {
                        rLock.unlock();//
                    }
                } else {
                    //锁被别人拿走了
                    //查询缓存
                    try {
                        TimeUnit.SECONDS.sleep(1);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    return (SkuInfo) redisTemplate.opsForValue().get(cacheKey);
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        return skuInfo;
    }
    //查询SkuInfo对象
/*    @Override
    public SkuInfo getSkuInfo(Long skuId) {
        //1:查询Redis  K: sku:skuId:info
        String cacheKey = RedisConst.SKUKEY_PREFIX + skuId + RedisConst.SKUKEY_SUFFIX;
        //锁的Key
        String lockKey = RedisConst.SKUKEY_PREFIX + skuId + RedisConst.SKULOCK_SUFFIX;

        SkuInfo skuInfo = (SkuInfo) redisTemplate.opsForValue().get(cacheKey);
        if (null == skuInfo) {
            //上锁  原子性操作
            String uuid = UUID.randomUUID().toString();
            Boolean isFlag = redisTemplate.opsForValue().setIfAbsent(lockKey, uuid,
                    1, TimeUnit.SECONDS);
            if (isFlag) {
                //3:没有 直接查询DB
                skuInfo = skuInfoMapper.selectById(skuId);
                //判断是否NULL
                if (null != skuInfo) {
                    List<SkuImage> skuImageList = skuImageMapper.
                            selectList(new QueryWrapper<SkuImage>().eq("sku_id", skuId));
                    skuInfo.setSkuImageList(skuImageList);
                    //4:保存缓存中一份 100万次-1
                    redisTemplate.opsForValue().set(cacheKey, skuInfo,
                            RedisConst.SKUKEY_TIMEOUT + new Random().nextInt(300)
                            , TimeUnit.SECONDS);
                } else {
                    //4:保存缓存中一份  空结果也进行缓存，但它的过期时间会很短，最长不超过五分钟。
                    skuInfo = new SkuInfo();
                    redisTemplate.opsForValue().set(cacheKey, skuInfo, 5
                            , TimeUnit.MINUTES);
                }
                //删除锁 原子性操作
                String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return tostring(redis.call('del',KEYS[1])) else return 0 end";
                this.redisTemplate.execute(new DefaultRedisScript<>(script), Collections.singletonList(lockKey), uuid);

            } else {
                //查询缓存
                try {
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                return (SkuInfo) redisTemplate.opsForValue().get(cacheKey);
            }
        }
        return skuInfo;
    }*/

    //查询价格 是不可以优化到缓存中去的
    @Override
    public BigDecimal getPrice(Long skuId) {
        SkuInfo skuInfo = skuInfoMapper.selectById(skuId);
        if (null != skuInfo) {
            return skuInfo.getPrice();
        }
        return null;
    }

    //查询当前商品spu对应的所有的销售属性及属性值集合 并 skuid进行选中
    @Override
    public List<SpuSaleAttr> getSpuSaleAttrAndChecked(Long spuId, Long skuId) {
        return spuSaleAttrMapper.getSpuSaleAttrAndChecked(spuId, skuId);
    }

    //销售属性值ID的组合 对应 skuId 数据
    @Override
    public Map getSaleAttrValuesBySpu(Long spuId) {
        Map result = new HashMap<>();
        List<Map> listMap = skuSaleAttrValueMapper.getSaleAttrValuesBySpu(spuId);
        //将上面ListMap转成Map  性能？  不会   Java  -差-> .class --> runtime
        listMap.forEach(map -> {
            result.put(map.get("value_id"), map.get("sku_id"));
        });
        return result;
    }

    @Override
    public List<BaseCategoryView> getBaseCategoryViewList() {
        return baseCategoryViewMapper.selectList(null);
    }

    @Override
    public List<SkuAttrValue> getBaseAttrInfoAndBaseAttrValue(Long skuId) {
        return skuAttrValueMapper.getBaseAttrInfoAndBaseAttrValue(skuId);
    }

    @Autowired
    private SpuSaleAttrMapper spuSaleAttrMapper;
    @Autowired
    private SkuSaleAttrValueMapper skuSaleAttrValueMapper;
    @Autowired
    private SkuAttrValueMapper skuAttrValueMapper;
}
